// Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <dirent.h>
#include <include/utility.h>
#include <iostream>
#include <ostream>
#include <sys/stat.h>
#include <sys/types.h>
#include <vector>

namespace PaddleVideo
{

    std::vector<std::string> Utility::ReadDict(const std::string &path)
    {
        std::ifstream in(path);
        std::string line;
        std::vector<std::string> m_vec;
        if (in)
        {
            while (getline(in, line))
            {
                m_vec.push_back(line);
            }
        }
        else
        {
            std::cout << "no such label file: " << path << ", exit the program..."
                      << std::endl;
            exit(1);
        }
        return m_vec; // Use fstream to read the category list and return with vector
    }

    void Utility::GetAllFiles(const char *dir_name, std::vector<std::string> &all_inputs)
    {
        if (NULL == dir_name)
        {
            std::cout << " dir_name is null ! " << std::endl;
            return;
        }
        struct stat s;
        lstat(dir_name, &s);
        if (!S_ISDIR(s.st_mode))
        {
            std::cout << "dir_name is not a valid directory !" << std::endl;
            all_inputs.push_back(dir_name);
            return;
        }
        else
        {
            struct dirent *filename; // return value for readdir()
            DIR *dir;                // return value for opendir()
            dir = opendir(dir_name);
            if (NULL == dir)
            {
                std::cout << "Can not open dir " << dir_name << std::endl;
                return;
            }
            std::cout << "Successfully opened the dir !" << std::endl;
            while ((filename = readdir(dir)) != NULL)
            {
                if (strcmp(filename->d_name, ".") == 0 ||
                    strcmp(filename->d_name, "..") == 0)
                    continue;
                // img_dir + std::string("/") + all_inputs[0];
                all_inputs.push_back(dir_name + std::string("/") +
                                     std::string(filename->d_name));
            }
        }
    }

    cv::Mat Utility::GetRotateCropImage(const cv::Mat &srcimage, std::vector<std::vector<int>> box)
    {
        cv::Mat image;
        srcimage.copyTo(image);
        std::vector<std::vector<int>> points = box;

        int x_collect[4] = {box[0][0], box[1][0], box[2][0], box[3][0]};
        int y_collect[4] = {box[0][1], box[1][1], box[2][1], box[3][1]};
        int left = int(*std::min_element(x_collect, x_collect + 4));
        int right = int(*std::max_element(x_collect, x_collect + 4));
        int top = int(*std::min_element(y_collect, y_collect + 4));
        int bottom = int(*std::max_element(y_collect, y_collect + 4));

        cv::Mat img_crop;
        image(cv::Rect(left, top, right - left, bottom - top)).copyTo(img_crop);

        for (int i = 0; i < points.size(); i++)
        {
            points[i][0] -= left;
            points[i][1] -= top;
        }

        int img_crop_width = int(sqrt(pow(points[0][0] - points[1][0], 2) +
                                      pow(points[0][1] - points[1][1], 2)));
        int img_crop_height = int(sqrt(pow(points[0][0] - points[3][0], 2) +
                                       pow(points[0][1] - points[3][1], 2)));

        cv::Point2f pts_std[4];
        pts_std[0] = cv::Point2f(0., 0.);
        pts_std[1] = cv::Point2f(img_crop_width, 0.);
        pts_std[2] = cv::Point2f(img_crop_width, img_crop_height);
        pts_std[3] = cv::Point2f(0.f, img_crop_height);

        cv::Point2f pointsf[4];
        pointsf[0] = cv::Point2f(points[0][0], points[0][1]);
        pointsf[1] = cv::Point2f(points[1][0], points[1][1]);
        pointsf[2] = cv::Point2f(points[2][0], points[2][1]);
        pointsf[3] = cv::Point2f(points[3][0], points[3][1]);

        cv::Mat M = cv::getPerspectiveTransform(pointsf, pts_std);

        cv::Mat dst_img;
        cv::warpPerspective(img_crop, dst_img, M,
                            cv::Size(img_crop_width, img_crop_height),
                            cv::BORDER_REPLICATE);

        if (float(dst_img.rows) >= float(dst_img.cols) * 1.5)
        {
            cv::Mat srcCopy = cv::Mat(dst_img.rows, dst_img.cols, dst_img.depth());
            cv::transpose(dst_img, srcCopy);
            cv::flip(srcCopy, srcCopy, 0);
            return srcCopy;
        }
        else
        {
            return dst_img;
        }
    }

    std::vector<cv::Mat> Utility::SampleFramesFromVideo(const std::string &VideoPath, const int &num_seg, const int &seg_len)
    {
        cv::VideoCapture capture(VideoPath); // Create a video object
        if (!capture.isOpened())
        {
            printf("[Error] video cannot be opened, please check the video [%s]\n", VideoPath.c_str());
            capture.release();
            exit(1);
        }

        int frames_len = capture.get(cv::CAP_PROP_FRAME_COUNT); // Get the total number of video frames

        int average_dur = int(frames_len / num_seg);
        std::vector<int> frames_idx;

        for (int i = 0; i < num_seg; ++i)
        {
            int idx = 0;
            if (average_dur >= seg_len)
            {
                idx = (average_dur - 1) / 2;
                idx += i * average_dur;
            }
            else if (average_dur >= 1)
            {
                idx += i * average_dur;
            }
            else
            {
                idx = i;
            }
            for (int j = idx; j < idx + seg_len; ++j)
            {
                frames_idx.emplace_back(j % frames_len);
            }
        }
        std::vector<cv::Mat> sampled_frames;
        cv::Mat frame; // Create an object for storing sampled frames
        for (int i = 0; i < num_seg; ++i)
        {
            const int &frame_idx = frames_idx[i];
            capture.set(cv::CAP_PROP_POS_FRAMES, frame_idx); // Set to frame_idx frame
            capture >> frame;
            sampled_frames.push_back(frame);
        }
        capture.release(); // Release the video object
        return sampled_frames;
    }
} // namespace PaddleVideo
